﻿#pragma execution_character_set("utf-8")
#include <QUdpSocket>
#include <QTcpServer>
#include <QTcpSocket>
#include <QtNetwork/QNetworkInterface>
#include <QDebug>
#include <QPair>
#include <QTimer>
#include "systimer.h"
#include "udpsys.h"
#include "reporter.h"
#include "testsetup.h"
#include "myhelper.h"
#include "stoast.h"
#include "messagebox.h"
#include "devicesetup.h"

bool isStartTestCase;           //是否开始测试
Systimer::Systimer()
{
    isStartTestCase = false;        // 未开始测试
    isTestendPoweroff = true;       // 测试完成后关机

    reporter        = new Reporter(nullptr);           // 测试报告生成管理类
    testsetup       = new TestSetup(nullptr);          // 测试配置类

    /* UDP网络报文初始化 */
    udpSocket_1     = new QUdpSocket(this);
    udpSocket_2     = new QUdpSocket(this);
    connect(udpSocket_1, SIGNAL(readyRead()), this, SLOT(socketDataReceived_1()));
    connect(udpSocket_2, SIGNAL(readyRead()), this, SLOT(socketDataReceived_2()));
    bool result_1 = udpSocket_1->bind(PORT_UDP_COMM_1);
    if(!result_1){
        qDebug() << "udpsocket_1 bind error!";
    }else{
        qDebug() << "udpsocket_1 bind success!";
    }
    bool result_2 = udpSocket_2->bind(PORT_UDP_COMM_2);
    if(!result_2){
        qDebug() << "udpsocket_2 bind error!";
    }else{
        qDebug() << "udpsocket_2 bind success!";
    }
    appendLog(QString("socket1 bind: %1, port: %2").arg(result_1).arg(PORT_UDP_COMM_1), LOG_DEBUG);
    appendLog(QString("socket2 bind: %1, port: %2").arg(result_2).arg(PORT_UDP_COMM_2), LOG_DEBUG);

    /* TCP网络报文初始化 */
    tcpServer = new QTcpServer();
    connect(tcpServer, &QTcpServer::newConnection, this, &Systimer::onTcpserver_newConnect);
    if(!tcpServer->listen(QHostAddress::Any, PORT_TCP_COMM_1)){
        qDebug() << tcpServer->errorString();
    }else{
        qDebug() << "tcpserver_1 listen-" << PORT_TCP_COMM_1 << " success!" << endl;
    }

    /* 网络IP地址检测配置 */
    ipDevList = getLocalmachineIP();
    QTimer::singleShot(1200, this, SLOT(onInitLater()));

    appendLog(QString("Systimer started, local_ip: %1").arg(this->localIP), LOG_INFO);
    timerID = startTimer(800);

    /* 设备设置测试 */
    devicesetup = new DeviceSetup;
    connect(devicesetup, SIGNAL(startOneOrder(int,QString,int)), this, SLOT(starteOneOrder(int,QString,int)));
    connect(devicesetup, SIGNAL(startOneOrder_udp(int,QString,int)), this, SLOT(starteOneOrder_udp(int,QString,int)));
}

Systimer::~Systimer()
{
    if(timerID != 0){
        killTimer(timerID);
    }
}



void Systimer::socketDataReceived_1()
{
    processUdpData(udpSocket_1);
}

void Systimer::socketDataReceived_2()
{
    processUdpData(udpSocket_2);
}

void Systimer::onTcpserver_newConnect()
{
    // 获取客户端连接
    QTcpSocket *tcpSocket = tcpServer->nextPendingConnection();

    // 连接tcpsocket的信号槽
    connect(tcpSocket, &QTcpSocket::readyRead, this, &Systimer::onTcpserver_dataReceived);
    connect(tcpSocket, &QTcpSocket::disconnected, this, &Systimer::onTcpserver_disConnect);

    QString ipstr = UdpSys::getHostIP(tcpSocket->peerAddress());
    appendLog(QString("A client connect: %1-%2").arg(ipstr).arg((long long)tcpSocket), LOG_INFO);
    tcpSocketList.push_back(tcpSocket);
    tcpSocketIPList.push_back(ipstr);
}

void Systimer::onTcpserver_dataReceived()
{
    QTcpSocket* tcpSocket = qobject_cast<QTcpSocket*>(sender());
    processTcpData(tcpSocket);
}

///
/// \brief Systimer::onTcpserver_disConnect
/// tcpserver检测到有客户端关闭连接
///
void Systimer::onTcpserver_disConnect()
{
    QTcpSocket* tcpSocket = qobject_cast<QTcpSocket*>(sender());

    QString ipstr = UdpSys::getHostIP(tcpSocket->peerAddress());
    // qDebug() << "A client disconnect: " << ipstr << "-" << tcpSocket;
    appendLog(QString("A client disconnect: %1-%2").arg(ipstr).arg((long long)tcpSocket), LOG_INFO);
    tcpSocket->close();

    int index = tcpSocketList.indexOf(tcpSocket);
    if(index >= 0){
        tcpSocketList.removeAt(index);
        tcpSocketIPList.removeAt(index);
    }
    tcpSocket->deleteLater();
}

void Systimer::onInitLater()
{
    emit networkConfig(ipDevList.length()>0?ipDevList.at(0):"",
                       ipDevList.length()>1?ipDevList.at(1):"",
                       ipDevList.length()>2?ipDevList.at(2):"");
    /* 测试配置文件读取 */
    loadTestScript("", "");
}

void Systimer::loadTestScript(QString devName, QString fileName)
{
    MyXML myxml;
    if(devName.length() > 0 && fileName.length() > 0){
        if (fileName.startsWith("file:///")) {
            fileName = fileName.right(fileName.length() - 8);
        }
        setupcase.testFile = fileName;
        setupcase.devName  = devName;
        myxml.SetSetupDetail(setupcase);
    }
    setupcase = myxml.GetSetupDetail();
    qDebug() << "devNames: \t" << setupcase.devName;
    qDebug() << "testFile: \t" << setupcase.testFile;

    if(myxml.OpenXML(setupcase.testFile)){
        testCaseList = myxml.GetDetail();
        for(int i=0; i<testCaseList.length(); i++){
            qDebug() << "name   :\t" << testCaseList.at(i).name;
            qDebug() << "cmd    :\t" << testCaseList.at(i).cmd;
            qDebug() << "counts :\t" << testCaseList.at(i).counts;
            qDebug() << "timeMax:\t" << testCaseList.at(i).timeMax;
            qDebug() << "resultStart:\t" << testCaseList.at(i).resultStart;
            qDebug() << "resultEnd:\t"   << testCaseList.at(i).resultEnd;
            qDebug() << "unitStr:\t"   << testCaseList.at(i).unitStr;
            qDebug() << " ";
        }
    }

    testCasestrModel.clear();
    for (int i = 0; i < TEST_DEV_NUM; i++) {
        testCasestrModel.append(getTestcaseDetail(i));
    }
    emit testcaseConfig(testCaseList.length());

    // 测试总的预计时间计算
    setupcase.totalOutTimes = 0;
    setupcase.totalTestCounts = 0;
    for(int i=0; i<testCaseList.length(); i++){
        setupcase.totalOutTimes += testCaseList.at(i).timeMax * testCaseList.at(i).counts;
        setupcase.totalTestCounts += testCaseList.at(i).counts;
    }
}

QStringList Systimer::getTestcaseDetail(int devIndex)
{
    QStringList retStrList;
    if (devIndex < udpDeviceList.length()) {
        for (int i = 0; i < testCaseList.length(); i++) {
            if (!isStartTestCase || i > udpDeviceList.at(devIndex).cmdIndex) {
                retStrList.append(QString("%1[%2/%3]").arg(testCaseList.at(i).name)
                    .arg(0)
                    .arg(testCaseList.at(i).counts));
            }
            else if (i == udpDeviceList.at(devIndex).cmdIndex) {
                retStrList.append(QString("%1[%2/%3]").arg(testCaseList.at(i).name)
                    .arg(udpDeviceList.at(devIndex).testCounts)
                    .arg(testCaseList.at(i).counts));
            }
            else {
                retStrList.append(QString("%1[%2/%3]").arg(testCaseList.at(i).name)
                    .arg(testCaseList.at(i).counts)
                    .arg(testCaseList.at(i).counts));
            }
        }
    } else {
        for (int i = 0; i < testCaseList.length(); i++) {
            retStrList.append(QString("%1[%2/%3]").arg(testCaseList.at(i).name)
                .arg(0)
                .arg(testCaseList.at(i).counts));
        }
    }
    return retStrList;
}

void Systimer::testCaseSetup()
{
    testsetup->CreateTestSetup(&setupcase, &testCaseList);
}

QStringList Systimer::getLocalmachineIP()
{
    QStringList ipAddressList;
    QString     ipAddress;
    QList<QHostAddress> hostAddressesList = QNetworkInterface::allAddresses();
    // use the first non-localhost IPv4 address
    for (int i = 0; i < hostAddressesList.size(); ++i) {
        if (hostAddressesList.at(i) != QHostAddress::LocalHost &&
            hostAddressesList.at(i).toIPv4Address()) {
            ipAddress = hostAddressesList.at(i).toString();
//            if(ipAddress.startsWith("192.168"))
                ipAddressList.append(ipAddress);
        }
    }
    return ipAddressList;
}

int Systimer::getIndexFromUList(const QList<UdpDevice> &uDList, QString macStr)
{
    int length    = uDList.length();
    int macLength =  macStr.length();
    macLength /= 2;
    if(length <= 0)
        return -1;
    for(int i=0; i<length; i++){
        if(uDList.at(i).macStr.contains(macStr.left(macLength)) &&
                uDList.at(i).macStr.contains(macStr.right(macLength))){
            return i;
        }
    }
    return -1;
}

int Systimer::getIndexFromUListIP(const QList<UdpDevice> &uDList, QString ipStr)
{
    int length = uDList.length();
    if(length <= 0)
        return -1;
    for(int i=0; i<length; i++){
        if(0 == ipStr.compare(uDList.at(i).ipAddress)){
            return i;
        }
    }
    return -1;
}

void Systimer::appendLog(QString logstr, int loglevel)
{
    switch (loglevel) {
    case LOG_DEBUG:
        qDebug() << QTime::currentTime().toString("hh:mm:ss ") << logstr;
        break;
    case LOG_INFO:
        qInfo() << QTime::currentTime().toString("hh:mm:ss ") << logstr;
        break;
    case LOG_WARN:
        qWarning() << QTime::currentTime().toString("hh:mm:ss ") << logstr;
        break;
    case LOG_ERR:
        qCritical() << QTime::currentTime().toString("hh:mm:ss ") << logstr;
        break;
    default:
        return;
    }
}

QString Systimer::getResultFormOUT(QString outstr, QString cmdStart, QString cmdEnd, int cmdIndex)
{
    int length  = outstr.length();
    int index   = outstr.indexOf(cmdStart);
    if(index < 0)
        return "NULL-start";
    index += cmdStart.length();
    if(index >= length)
        return "NULL-start";

    QString retStr = outstr.mid(index, length-index);
    index = retStr.indexOf(cmdEnd);
    if(index < 0)
        return "NULL-end";
    retStr = retStr.mid(0, index).trimmed();
    if(testCaseList.at(cmdIndex).unitStr.contains("无")){
        return retStr;
    }else{
        float retNum = retStr.toFloat();
        if(retNum < testCaseList.at(cmdIndex).resultLower){
            retStr.append("(偏低)");
        }else if( retNum > testCaseList.at(cmdIndex).resultUpper){
            retStr.append("(偏高)");
        }
    }
    return retStr;
}

QString Systimer::resultHandle(QString result, int cmdIndex)
{
    if(!result.contains("ok"))
        return result;

    int counts = 0;
    while(result.contains("ok")){
        int index = result.indexOf("ok");
        result = result.right(result.length()-index-2);
        counts++;
    }
    QString retStr = QString("ok:%1").arg(counts);
    if(counts < testCaseList.at(cmdIndex).resultLower*testCaseList.at(cmdIndex).counts
            || counts > testCaseList.at(cmdIndex).resultUpper*testCaseList.at(cmdIndex).counts){
        retStr.append("\n(异常)");
    }
    return retStr;
}



void Systimer::starteOneOrder(int box_index, QString cmdstr, int timeOuts)
{
    QString clientAddr = udpDeviceList.at(box_index).ipAddress;
    bool sendOk = false;
    if(clientAddr.length() > 0){
        // 首先尝试TCP发送
        while(true){
            int index = tcpSocketIPList.indexOf(clientAddr);
            if(index >= 0){
                QString ipstr = UdpSys::getHostIP(tcpSocketList.at(index)->peerAddress());
                if(0 != clientAddr.compare(ipstr) ){
                    appendLog(QString("警告: 探测到目标IP[%1]TCP连接异常, now conn IP[].").arg(clientAddr).arg(ipstr),
                                LOG_WARN);
                    tcpSocketList.at(index)->close();
                    delete tcpSocketList.at(index);
                    tcpSocketList.removeAt(index);
                    tcpSocketIPList.removeAt(index);
                }else{
                    // 开始TCP报文发送
                    sendOk = UdpSys::sendDataGram_toAddress(tcpSocketList.at(index), clientAddr, CMD_RUNCMD, timeOuts, cmdstr, "");
                    break;
                }
            }else{
                break;
            }
        }

        // 若TCP发送失败，则用UDP
        if(false == sendOk){

            UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_1, clientAddr,
                                           CMD_RUNCMD, timeOuts, cmdstr, "");
            UdpSys::sendDataGram_toAddress(udpSocket_2, PORT_UDP_COMM_2, clientAddr,
                                           CMD_RUNCMD, timeOuts, cmdstr, "");
        }

        // 记录命令执行日志
        QString logstr = QString("cmd(%1) to box_%2, ip:%3-%4").arg(cmdstr).arg(box_index).arg(clientAddr).arg(sendOk?"TCP":"UDP");
        if(udpDeviceList.at(box_index).isReboot)
            appendLog(logstr, LOG_WARN);
        else
            appendLog(logstr, LOG_DEBUG);
    }
}

void Systimer::starteOneOrder_udp(int box_index, QString cmdstr, int timeOuts)
{
    QString clientAddr = udpDeviceList.at(box_index).ipAddress;
    bool sendOk = false;
    if(clientAddr.length() > 0){
        UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_1, clientAddr,
                                       CMD_RUNCMD, timeOuts, cmdstr, "");
        UdpSys::sendDataGram_toAddress(udpSocket_2, PORT_UDP_COMM_2, clientAddr,
                                       CMD_RUNCMD, timeOuts, cmdstr, "");
        // 记录命令执行日志
        QString logstr = QString("cmd(%1) to box_%2, ip:%3-%4").arg(cmdstr).arg(box_index).arg(clientAddr).arg(sendOk?"TCP":"UDP");
        if(udpDeviceList.at(box_index).isReboot)
            appendLog(logstr, LOG_WARN);
        else
            appendLog(logstr, LOG_DEBUG);
    }
}

static int     later_index      = 0;
static QString later_cmdstr     = "";
static int     later_timeOuts   = 0;
// 延时发送udp报文
void Systimer::starteLater()
{
    starteOneOrder(later_index, later_cmdstr, later_timeOuts);
}


void Systimer::starteOneOrderLater(int box_index, QString cmdstr, int timeOuts, int delay_S)
{
    later_index     = box_index;
    later_cmdstr    = cmdstr;
    later_timeOuts  = timeOuts;
    QTimer::singleShot(delay_S*1000, this, SLOT(starteLater()));
}

void Systimer::setLocalIPAddress(QString ipstr)
{
    this->localIP = ipstr;
    for(int i=0; i<3; i++){
        ipstr.chop(1);
        if(ipstr.endsWith("."))
            break;
    }
    if(ipstr.endsWith(".")){
        ipstr.append("1");
        this->localGateway = ipstr;
        ipstr.chop(1);
        ipstr.append("255");
        this->localBroadcast = ipstr;
    }
    appendLog(QString("IP:%1, GATEWAY:%2, BROADCAST:%3")
              .arg(localIP).arg(localGateway).arg(localBroadcast), LOG_INFO);
}

void Systimer::pingTest(QString ipstr)
{
    QString logStr;
    static bool reEntry = false;
    if(reEntry){
        return;
    }
    reEntry = true;

    emit pingTestRet("");
    QTextCodec *codec = QTextCodec::codecForName("GBK");
    QProcess pingExec;
    QString cmdstr = "ping " + ipstr;
    pingExec.start(cmdstr);
    for(int i=0; i<60; i++){           //6S
        SingleTonSToast::msleep(100);
        QString outStr = codec->toUnicode(pingExec.readAll());

        if(outStr.length() > 0){
            logStr.append(outStr);
            emit pingTestRet(outStr);
        }
        if(pingExec.state() == QProcess::NotRunning){
            break;
        }
    }
    if(-1 != logStr.indexOf("TTL=")){
        SingleTonSToast::toast("网络连接测试成功");
    }else{
        SingleTonSToast::toast("网络连接失败，请检查网关地址是否配置正确", 3500);     //3500ms
    }
    pingExec.close();
    reEntry = false;
}

void Systimer::autoTestSwitch(bool isStart, bool isManual)
{
    if(isStart){        // 开始老化测试
        if(isStartTestCase){
            appendLog("already start test!!!", LOG_DEBUG);
            return;
        }else{
            appendLog("start autoTest...", LOG_DEBUG);
            isStartTestCase = true;
            for(int i=0; i<udpDeviceList.length(); i++){
                udpDeviceList[i].cmdIndex = 0;         // 设置从第1项开始测试
                udpDeviceList[i].isTestEnded = false;
                udpDeviceList[i].status = STAT_NORMAL;
                udpDeviceList[i].isRuning = false;
                udpDeviceList[i].testLogs.clear();
            }
            setupcase.startTime = QDateTime::currentDateTime();
            reporter->setVisible(false);
        }
    }else{              // 停止老化测试
        if(false == isStartTestCase){
            appendLog("already stoped test!!!", LOG_DEBUG);
            return;
        }else{
            setupcase.endTime = QDateTime::currentDateTime();
            isStartTestCase = false;
            reporter->CreateTestReport(setupcase, udpDeviceList, testCaseList);
            devicesetup->close();

            if(isManual){
                for(int i=0; i<udpDeviceList.length(); i++){
                    if(udpDeviceList.at(i).status == STAT_NORMAL)
                        emit testDevStatTrigger(i, LOG_WARN, "测试已手动终止");
                }
            }
            for(int i=0; i<udpDeviceList.length(); i++){
                udpDeviceList[i].cmdOutStr.clear();
                udpDeviceList[i].cmdResult.clear();
                udpDeviceList[i].testTimes.clear();
            }
            // 杀掉所有测试进程
            UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_1, this->localBroadcast,
                                           CMD_KILLALL, 0, "", "");
            UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_1, "255.255.255.255",
                                           CMD_KILLALL, 0, "", "");

        }
    }
}

void Systimer::turnOffAll(bool isReboot)
{
    QString infoStr;
    if(isReboot){
        infoStr = QString("确定对所有测试设备进行重启操作吗？");
    }else{
        infoStr = QString("确定对所有测试设备进行关机操作吗？\n关机之后若继续测试需要手动对BOX设备进行开机！！！");
    }
    if(myHelper::ShowMessageBoxQuesion(infoStr)){
        QString broadAddress = this->localBroadcast;
        if(broadAddress.length() > 0){
            QString cmdstr = (isReboot?"reboot":"poweroff");
            UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_1, broadAddress,
                                           CMD_RUNCMD, 0, cmdstr, "");
            UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_2, broadAddress,
                                           CMD_RUNCMD, 0, cmdstr, "");
            UdpSys::sendDataGram_toAddress(udpSocket_1, PORT_UDP_COMM_1, "255.255.255.255",
                                           CMD_RUNCMD, 0, cmdstr, "");
            UdpSys::sendDataGram_toAddress(udpSocket_2, PORT_UDP_COMM_2, "255.255.255.255",
                                           CMD_RUNCMD, 0, cmdstr, "");
            QString logstr = QString("cmd(%1) to dev_%2, ip:%3").arg(cmdstr).arg(255).arg(broadAddress);
            appendLog(logstr, LOG_WARN);
        }
    }else{
        SingleTonSToast::toast("操作已取消");
    }
}

void Systimer::setPoweroffAuto(bool isPoweroff)
{
    this->isTestendPoweroff = isPoweroff;
}

void Systimer::changeTestCase(QString filePath)
{
    appendLog("start change testcase filepath!!!", LOG_DEBUG);
    loadTestScript(setupcase.devName, filePath);
}

QString Systimer::getDevTestCaseStr(int devIndex, int cmdIndex, int cmdRepeatIndex)
{
    QString retstr;
    int testCounts = testCaseList.length();
    if (false == isStartTestCase) {
        // 尚未开始老化
        if (devIndex < udpDeviceList.length())
            retstr += QString("<font color=green>%1</font><br>").arg("等待开始测试");
        else
            retstr += QString("<font color=white>%1</font><br>").arg("等待设备连接");
    }else{
        // 已经开始老化
        retstr += QString("<font color=white>%1</font><br>")
                .arg(QString("进行中测试项:%1").arg(cmdIndex<testCounts?"":"无"));
    }
    // 添加待测试项或者正在进行的测试项
    for (int i = cmdIndex; i < testCounts; i++) {
        if (devIndex < udpDeviceList.length()) {
            if (false == isStartTestCase || i > cmdIndex ) {          // 尚未开始老化
                retstr += QString("%1.%2<br>").arg(i+1).arg(testCasestrModel.at(devIndex).at(i));
            } else{                                                   // 正在测试
                retstr += QString("<font color=green>%1.%2[%3/%4]</font><br>")
                        .arg(i+1).arg(testCaseList.at(i).name)
                        .arg(cmdRepeatIndex).arg(testCaseList.at(i).counts);
            }
        } else {
            retstr += testCasestrModel.at(devIndex).at(i) + "<br>";
        }
    }
    // 添加已完成的测试项
    if(isStartTestCase){
        // 已经开始老化
        retstr += QString("<font color=white>%1</font><br>")
                .arg(QString("已完成测试项:%1").arg(cmdIndex>0?"":"无"));
        for (int i = 0; i < cmdIndex; i++) {
            if (devIndex < udpDeviceList.length()) {
                // 测试完成
                retstr += QString("<font color=black>%1.%2[%3/%4]</font><br>")
                        .arg(i+1).arg(testCaseList.at(i).name)
                        .arg(testCaseList.at(i).counts).arg(testCaseList.at(i).counts);
            } else {
                retstr += testCasestrModel.at(devIndex).at(i) + "<br>";
            }
        }
    }

    return retstr;
}

void Systimer::testDevControl(int devIndex)
{
    QStringList selectList;
    selectList << "重启设备" << "重新开始测试" << "取消";
    int select = Message::showSelect(NULL, QString("设备%1变更").arg(devIndex), "请选择对此设备进行的处理", selectList);

    if(select == 0){
        starteOneOrder(devIndex, "reboot", 50);
        SingleTonSToast::toast(QString("该设备已重启！"));
    }else if(select == 1){
        if(!isStartTestCase){
            Message::showWarning(NULL, "警告", "未开始老化，无法重新开始测试！");
            return;
        }

        SingleTonSToast::toast(QString("设备已重新开始测试"));
        udpDeviceList[devIndex].status = STAT_RESTART;
    }else{
        SingleTonSToast::toast(QString("取消了选择！"));
    }
}

void Systimer::testDevSetup(int devIndex)
{
    devicesetup->SetDeviceInfo(&udpDeviceList[devIndex], devIndex);
    devicesetup->show();
}
